package com.modernyuanshiren.withwinds;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.modernyuanshiren.AbstractStatus;
import com.modernyuanshiren.GameMap;
import com.modernyuanshiren.IAction;
import com.modernyuanshiren.IElement;
import com.modernyuanshiren.IPosition;

public class WindStatus extends AbstractStatus<WindStatus, WindSingleAction> {
	private boolean isSymmetry;

	@Override
	protected void setElements(List<IElement> elements) {
		super.setElements(elements);
		isSymmetry = checkSymmetry(this); 
	}

	@Override
	public boolean isSymmetry() {
		return isSymmetry;
	}

	@Override
	public boolean needCheckSymmetry() {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		return map.isHorizonSymmetry() || map.isVerticalSymmetry();
	}

	@Override
	public boolean checkSymmetry(WindStatus status) {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		boolean mapHSymm = map.isHorizonSymmetry();
		boolean mapVSymm = map.isVerticalSymmetry();
		boolean isHorizonSymmetry = false;
		if (mapHSymm) {
			isHorizonSymmetry = checkHorizonSymmetry(status);
		}
		boolean isVerticalSymmetry = false;
		if (mapVSymm) {
			isVerticalSymmetry = checkVerticalSymmetry(status);
		}
		return (mapHSymm && isHorizonSymmetry)
				|| (mapVSymm && isVerticalSymmetry);
	}

	private boolean checkVerticalSymmetry(WindStatus status) {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		int h = map.getHeight();
		return getAllElements()
				.stream()
				.distinct()
				.map(IElement::getPosition)
				.allMatch(
						pos -> {
							List<String> thisList = this
									.getAllElementByPosition(pos.getX(),
											pos.getY()).stream()
									.map(IElement::getName).sorted()
									.collect(Collectors.toList());
							List<String> otherList = status
									.getAllElementByPosition(pos.getX(),
											h - 1 - pos.getY()).stream()
									.map(IElement::getName).sorted()
									.collect(Collectors.toList());
							return thisList.equals(otherList);
						});
	}

	private boolean checkHorizonSymmetry(WindStatus status) {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		int w = map.getWidth();
		return getAllElements()
				.stream()
				.distinct()
				.map(IElement::getPosition)
				.allMatch(
						pos -> {
							List<String> thisList = this
									.getAllElementByPosition(pos.getX(),
											pos.getY()).stream()
									.map(IElement::getName).sorted()
									.collect(Collectors.toList());
							List<String> otherList = status
									.getAllElementByPosition(
											w - 1 - pos.getX(), pos.getY())
									.stream().map(IElement::getName).sorted()
									.collect(Collectors.toList());
							return thisList.equals(otherList);
						});
	}

	@Override
	public boolean isEnd() {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		IElement man = this.getElementByName(gameContext.getCharacterName())
				.get(0);
		GameMap map = gameContext.getMap();
		return map.is(man.getPosition(), gameContext.getTargetName());
	}

	@Override
	protected List<List<IElement>> getAllElementCombinations() {
		List<List<IElement>> result = new ArrayList<>();
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		IElement man = this.getElementByName(gameContext.getCharacterName())
				.get(0);
		List<IElement> list = new ArrayList<>();
		list.add(man);
		List<IElement> boxes = this.getElementByName(gameContext.getBoxName());

		Map<IElement, Long> counted = boxes
				.stream()
				.filter(e -> !map.is(e.getPosition(),
						gameContext.getDangerName()))
				.collect(
						Collectors.groupingBy(Function.identity(),
								Collectors.counting()));
		List<IElement> finalList = counted
				.keySet()
				.stream()
				.filter(e -> (!isHole(e, gameContext, map))
						|| counted.get(e) > 1).collect(Collectors.toList());
		list.addAll(finalList);
		result.add(list);
		return result;
	}

	private boolean isHole(IElement element, CommonWindGame gameContext,
			GameMap map) {
		return map.isOneOf(element.getPosition(),
				gameContext.getNotTargetHoleName(),
				gameContext.getTargetHoleName());
	}

	@Override
	protected List<WindSingleAction> getAllSingleActions() {
		return Arrays.asList(WindSingleAction.values());
	}

	@Override
	protected IAction<WindStatus, WindSingleAction> createAction(
			List<IElement> elements, WindSingleAction singleAction) {
		return new WindAction(elements, singleAction);
	}

	public long getBoxCountAt(IPosition position) {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		return getElementByName(gameContext.getBoxName()).stream()
				.filter(e -> e.getPosition().equals(position)).count();
	}

	public long getEmptyTargetCount() {
		CommonWindGame gameContext = CommonWindGame.getGameContext();
		GameMap map = gameContext.getMap();
		return map.getAllPositions(gameContext.getTargetHoleName()).stream()
				.filter(p -> getBoxCountAt(p) == 0).count();
	}
}
